1. Load your neuron in CATMAID and check the root of the neuron is at the soma and you have tagged all the nodes you want to prune from.
#Load all the necessary libraries
library(catmaid)
library(elmr)
skid <- 534333
n <- read.neuron.catmaid(skid) #aSPg neuron
Cut neuron at specified tagged nodes, in this case “AJES_cut_x”
#Get the nodes for the tags with "AJES_cut" in them
cut_nodes <- n$tags[grepl("AJES_cut", names(n$tags))]
#Reorder to AJES_cut_1, AJES_cut_2, AJES_cut_3, AJES_cut_4
cut_nodes <- cut_nodes[sort(names(cut_nodes))]
#Get X,Y,Z coords of cut_nodes to plot in 3d
n$d$X[n$d$PointNo %in% cut_nodes]
[1] 631533 632757 631631 629468
n$d$Y[n$d$PointNo %in% cut_nodes]
[1] 150459 149966 162402 158085
n$d$Z[n$d$PointNo %in% cut_nodes]
[1] 80000 81520 89720 91400
nview3d("frontal")
plot3d(n, soma = T, col = "black", WithConnectors = T)
col <- rainbow(length(cut_nodes))
sapply(1:4, function(x) points3d(n$d$X[n$d$PointNo %in% cut_nodes[x]],
n$d$Y[n$d$PointNo %in% cut_nodes[x]],
n$d$Z[n$d$PointNo %in% cut_nodes[x]],
size = 10,
col = col[x],
pch = 16)) #pch = 16 plots circles, use ?points to find other shapes plotted with the pch argument.
[1] 76 77 78 79
legend3d("topright", legend = names(cut_nodes), fill = col)
rglwidget()
#Lets start by cutting the neuron distal to AJES_cut_1.
dist <- distal_to(n, node.pointno = cut_nodes[[1]]) #Return indices of points in a neuron distal to a given node
#Subset our neuron by dist indices
neuron.distal.points <- n$d[dist,]
Plot points distal to cut point 1.
clear3d()
legend3d()
Error in as.graphicsAnnot(legend) :
argument "legend" is missing, with no default
nview3d("frontal")
plot3d(n, col = "gray23", soma = T)
points3d(neuron.distal.points[,c('X','Y','Z')], col = "deepskyblue")
rglwidget()
#That's great, but we are only interested in the axons on the left hemisphere. Not the ones on the right too.
#Lets remove everything distal to AJES_cut_2 from everything distal to AJES_cut1
#Lets start by cutting the neuron distal to AJES_cut_1.
dist1 <- distal_to(n, node.pointno = cut_nodes[[1]])
dist2 <- distal_to(n, node.pointno = cut_nodes[[2]])
#Subset our neuron by dist indices
neuron.distal.points <- n$d[setdiff(dist1, dist2),]
clear3d()
nview3d("frontal")
plot3d(n, col = "gray23", soma = T)
points3d(neuron.distal.points[,c('X','Y','Z')], col = "deepskyblue")
rglwidget()
# Or there's the option of including points distal to two cuts...
dist3 <- distal_to(n, node.pointno = cut_nodes[[3]])
dist4 <- distal_to(n, node.pointno = cut_nodes[[4]])
neuron.distal.points <- n$d[c(dist3, dist4),]
clear3d()
nview3d("frontal")
plot3d(n, col = "gray23", soma = T)
points3d(neuron.distal.points[,c('X','Y','Z')], col = "deepskyblue")
rglwidget()
Connectivity matrix of split neuron
#For our example lets just use the first cut point, and generate a connectivity matrix for the distal neuron
neuron.distal.points <- n$d[dist,]
#Generate connectivity matrix for neuron we are interested in
con_mat <- catmaid_get_connectors_between(post_skids = skid)
#subset connectivity matrix to only contain points in neuron.distal.points
neuron.distal.points #neuron.distal.points contains the following:
con_mat #and the connectivity matrix is a dataframe containing this informtation
#depending on whether you specified pre or post_skids in the catmaid_get_connectors_between we can subset con_mat by post_node_id or pre_node_id. In this example we are using post_skids and post_node_id to look at inputs to our neuron.
#subset con_mat by nodeid/PointNo from neuron.distal.points
con_mat$post_node_id %in% neuron.distal.points$PointNo #returns a logical matrix, TRUE values are connections in our distal branch.
[1] TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[19] FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[55] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[73] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[91] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[109] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[127] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[145] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[163] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[181] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[199] FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
[217] FALSE TRUE TRUE TRUE TRUE FALSE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE
con_mat[con_mat$post_node_id %in% neuron.distal.points$PointNo,] #don't forget the "," as con_mat is a dataframe.
dist_con_mat <- con_mat[con_mat$post_node_id %in% neuron.distal.points$PointNo,]
#We can even plot all of these connections to double check the connections are in the right place.
clear3d()
plot3d(n, soma = T, col = "grey")
points3d(neuron.distal.points[,c('X','Y','Z')], col = "deepskyblue")
points3d(dist_con_mat[,c("connector_x", "connector_y", "connector_z")], col = "green", size = 5)
rglwidget()
So you want to prune a neuron object instead?!
#Great! There's a handy function subset() in the nat package.
dist.n <- subset(n, dist)
names(dist.n)
[1] "NumPoints" "StartPoint" "BranchPoints" "EndPoints" "nTrees" "NumSegs" "SegList"
[8] "d"
names(n)
[1] "NumPoints" "StartPoint" "BranchPoints" "EndPoints" "nTrees" "NumSegs" "SegList"
[8] "d" "skid" "connectors" "tags" NA NA "url"
[15] "headers"
#note how there is less information in the dist.n neuron object. It loosess connectivity information.
Task/Challenge: Try generate a connectivity matrix for the other cut points
2. Use the following code to generate a sampling spreadsheet.
#gives random ordering of pre-synaptic profiles
node = 29496430
skid = 6065216
#function to return the incoming pre-synaptic nodes for the selected skid proximal of the provided cut point
incoming_connections <- function(skid, node) {
neuron <- read.neuron.catmaid(skid)
dist <- distal_to(neuron, node.pointno = node)
neuron.distal.points <- neuron$d[-dist,]
#debug- graph to check selected distal points are in correct region
nopen3d()
plot3d(neuron, col = "gray23")
points3d(neuron.distal.points[,c('X','Y','Z')], col = "deepskyblue")
all_connectors <- catmaid_get_connectors_between(post_skids = skid)
connectors = all_connectors[all_connectors$post_node_id %in% neuron.distal.points$PointNo,]
#debug - graph to check all selected connectors in correct region
nopen3d()
plot3d(neuron, col = "black")
points3d(connectors[,c('connector_x','connector_y','connector_z')])
return(connectors)
}
#get incoming synapses
n_incoming <- incoming_connections(skid = skid, node = node)
#randomise outgoing connectors for tracing downstream partners
perm = n_incoming[sample(nrow(n_incoming)),]
#debug -
nopen3d()
glX
14
neuron <- read.neuron.catmaid(skid)
plot3d(neuron)
points3d(n_incoming[,c('post_node_x','post_node_y','post_node_z')], col='blue')
points3d(perm[,c('post_node_x','post_node_y','post_node_z')], col = 'green', size = 8)
#—URL generator—
connector_URL <- function(dfrow){
base = "https://neuropil.janelia.org/tracing/fafb/v14/"
catmaid_url = paste0(base, "?pid=1")
catmaid_url = paste0(catmaid_url, "&zp=", dfrow[,"pre_node_z"])
catmaid_url = paste0(catmaid_url, "&yp=", dfrow[,"pre_node_y"])
catmaid_url = paste0(catmaid_url, "&xp=", dfrow[,"pre_node_x"])
catmaid_url = paste0(catmaid_url, "&tool=tracingtool")
catmaid_url = paste0(catmaid_url, "&active_skeleton_id=", dfrow[,'pre_skid'])
catmaid_url = paste0(catmaid_url, "&active_node_id=", dfrow[,"pre_node_id"])
catmaid_url = paste0(catmaid_url, "&sid0=5&s0=0")
invisible(catmaid_url)
}
#———
#generate URLs for each row
perm$URL = character(nrow(perm))
perm[,"URL"] = sapply(1:nrow(perm), function(x) perm[x, "URL"] = connector_URL(perm[x,]))
#write out as CSV to save in Google docs
write.csv(perm, file = 'test.csv')
library(googlesheets)
gs_ls()
Adding .httr-oauth to .gitignore
Waiting for authentication in browser...
Press Esc/Ctrl + C to abort
NA
LS0tCnRpdGxlOiAiU3BsaXR0aW5nIG5ldXJvbnMgaW50byBmcmFnbWVudHMgZm9yIHNhbXBsaW5nLCBhU1BnIGV4YW1wbGUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgMS4gTG9hZCB5b3VyIG5ldXJvbiBpbiBDQVRNQUlEIGFuZCBjaGVjayB0aGUgcm9vdCBvZiB0aGUgbmV1cm9uIGlzIGF0IHRoZSBzb21hIGFuZCB5b3UgaGF2ZSB0YWdnZWQgYWxsIHRoZSBub2RlcyB5b3Ugd2FudCB0byBwcnVuZSBmcm9tLgoKYGBge3J9CiNMb2FkIGFsbCB0aGUgbmVjZXNzYXJ5IGxpYnJhcmllcwpsaWJyYXJ5KGNhdG1haWQpCmxpYnJhcnkoZWxtcikKYGBgCgpgYGB7cn0Kc2tpZCA8LSA1MzQzMzMKbiA8LSByZWFkLm5ldXJvbi5jYXRtYWlkKHNraWQpICNhU1BnIG5ldXJvbgpgYGAKCiNDdXQgbmV1cm9uIGF0IHNwZWNpZmllZCB0YWdnZWQgbm9kZXMsIGluIHRoaXMgY2FzZSAiQUpFU19jdXRfeCIKYGBge3J9CiNHZXQgdGhlIG5vZGVzIGZvciB0aGUgdGFncyB3aXRoICJBSkVTX2N1dCIgaW4gdGhlbQpjdXRfbm9kZXMgPC0gbiR0YWdzW2dyZXBsKCJBSkVTX2N1dCIsIG5hbWVzKG4kdGFncykpXQojUmVvcmRlciB0byBBSkVTX2N1dF8xLCBBSkVTX2N1dF8yLCBBSkVTX2N1dF8zLCBBSkVTX2N1dF80CmN1dF9ub2RlcyA8LSBjdXRfbm9kZXNbc29ydChuYW1lcyhjdXRfbm9kZXMpKV0KI0dldCBYLFksWiBjb29yZHMgb2YgY3V0X25vZGVzIHRvIHBsb3QgaW4gM2QKbiRkJFhbbiRkJFBvaW50Tm8gJWluJSBjdXRfbm9kZXNdCm4kZCRZW24kZCRQb2ludE5vICVpbiUgY3V0X25vZGVzXQpuJGQkWltuJGQkUG9pbnRObyAlaW4lIGN1dF9ub2Rlc10KYGBgCmBgYHtyfQpudmlldzNkKCJmcm9udGFsIikKcGxvdDNkKG4sIHNvbWEgPSBULCBjb2wgPSAiYmxhY2siLCBXaXRoQ29ubmVjdG9ycyA9IFQpCmNvbCA8LSByYWluYm93KGxlbmd0aChjdXRfbm9kZXMpKQpzYXBwbHkoMTo0LCBmdW5jdGlvbih4KSBwb2ludHMzZChuJGQkWFtuJGQkUG9pbnRObyAlaW4lIGN1dF9ub2Rlc1t4XV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuJGQkWVtuJGQkUG9pbnRObyAlaW4lIGN1dF9ub2Rlc1t4XV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4kZCRaW24kZCRQb2ludE5vICVpbiUgY3V0X25vZGVzW3hdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjb2xbeF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBjaCA9IDE2KSkgI3BjaCA9IDE2IHBsb3RzIGNpcmNsZXMsIHVzZSA/cG9pbnRzIHRvIGZpbmQgb3RoZXIgc2hhcGVzIHBsb3R0ZWQgd2l0aCB0aGUgcGNoIGFyZ3VtZW50LgpsZWdlbmQzZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBuYW1lcyhjdXRfbm9kZXMpLCBmaWxsID0gY29sKQpyZ2x3aWRnZXQoKQpgYGAKCgpgYGB7cn0KI0xldHMgc3RhcnQgYnkgY3V0dGluZyB0aGUgbmV1cm9uIGRpc3RhbCB0byBBSkVTX2N1dF8xLgpkaXN0IDwtIGRpc3RhbF90byhuLCBub2RlLnBvaW50bm8gPSBjdXRfbm9kZXNbWzFdXSkgI1JldHVybiBpbmRpY2VzIG9mIHBvaW50cyBpbiBhIG5ldXJvbiBkaXN0YWwgdG8gYSBnaXZlbiBub2RlCiNTdWJzZXQgb3VyIG5ldXJvbiBieSBkaXN0IGluZGljZXMKbmV1cm9uLmRpc3RhbC5wb2ludHMgPC0gbiRkW2Rpc3QsXQpgYGAKI1Bsb3QgcG9pbnRzIGRpc3RhbCB0byBjdXQgcG9pbnQgMS4KYGBge3J9CmNsZWFyM2QoKQpsZWdlbmQzZCgpCm52aWV3M2QoImZyb250YWwiKQpwbG90M2QobiwgY29sID0gImdyYXkyMyIsIHNvbWEgPSBUKQpwb2ludHMzZChuZXVyb24uZGlzdGFsLnBvaW50c1ssYygnWCcsJ1knLCdaJyldLCBjb2wgPSAiZGVlcHNreWJsdWUiKQpyZ2x3aWRnZXQoKQpgYGAKCmBgYHtyfQojVGhhdCdzIGdyZWF0LCBidXQgd2UgYXJlIG9ubHkgaW50ZXJlc3RlZCBpbiB0aGUgYXhvbnMgb24gdGhlIGxlZnQgaGVtaXNwaGVyZS4gTm90IHRoZSBvbmVzIG9uIHRoZSByaWdodCB0b28uCiNMZXRzIHJlbW92ZSBldmVyeXRoaW5nIGRpc3RhbCB0byBBSkVTX2N1dF8yIGZyb20gZXZlcnl0aGluZyBkaXN0YWwgdG8gQUpFU19jdXQxCiNMZXRzIHN0YXJ0IGJ5IGN1dHRpbmcgdGhlIG5ldXJvbiBkaXN0YWwgdG8gQUpFU19jdXRfMS4KZGlzdDEgPC0gZGlzdGFsX3RvKG4sIG5vZGUucG9pbnRubyA9IGN1dF9ub2Rlc1tbMV1dKQpkaXN0MiA8LSBkaXN0YWxfdG8obiwgbm9kZS5wb2ludG5vID0gY3V0X25vZGVzW1syXV0pCgojU3Vic2V0IG91ciBuZXVyb24gYnkgZGlzdCBpbmRpY2VzCm5ldXJvbi5kaXN0YWwucG9pbnRzIDwtIG4kZFtzZXRkaWZmKGRpc3QxLCBkaXN0MiksXQoKYGBgCgpgYGB7cn0KY2xlYXIzZCgpCm52aWV3M2QoImZyb250YWwiKQpwbG90M2QobiwgY29sID0gImdyYXkyMyIsIHNvbWEgPSBUKQpwb2ludHMzZChuZXVyb24uZGlzdGFsLnBvaW50c1ssYygnWCcsJ1knLCdaJyldLCBjb2wgPSAiZGVlcHNreWJsdWUiKQpyZ2x3aWRnZXQoKQoKYGBgCgpgYGB7cn0KIyBPciB0aGVyZSdzIHRoZSBvcHRpb24gb2YgaW5jbHVkaW5nIHBvaW50cyBkaXN0YWwgdG8gdHdvIGN1dHMuLi4KZGlzdDMgPC0gZGlzdGFsX3RvKG4sIG5vZGUucG9pbnRubyA9IGN1dF9ub2Rlc1tbM11dKQpkaXN0NCA8LSBkaXN0YWxfdG8obiwgbm9kZS5wb2ludG5vID0gY3V0X25vZGVzW1s0XV0pCm5ldXJvbi5kaXN0YWwucG9pbnRzIDwtIG4kZFtjKGRpc3QzLCBkaXN0NCksXQpgYGAKCmBgYHtyfQpjbGVhcjNkKCkKbnZpZXczZCgiZnJvbnRhbCIpCnBsb3QzZChuLCBjb2wgPSAiZ3JheTIzIiwgc29tYSA9IFQpCnBvaW50czNkKG5ldXJvbi5kaXN0YWwucG9pbnRzWyxjKCdYJywnWScsJ1onKV0sIGNvbCA9ICJkZWVwc2t5Ymx1ZSIpCnJnbHdpZGdldCgpCmBgYAoKI0Nvbm5lY3Rpdml0eSBtYXRyaXggb2Ygc3BsaXQgbmV1cm9uCmBgYHtyfQojRm9yIG91ciBleGFtcGxlIGxldHMganVzdCB1c2UgdGhlIGZpcnN0IGN1dCBwb2ludCwgYW5kIGdlbmVyYXRlIGEgY29ubmVjdGl2aXR5IG1hdHJpeCBmb3IgdGhlIGRpc3RhbCBuZXVyb24KbmV1cm9uLmRpc3RhbC5wb2ludHMgPC0gbiRkW2Rpc3QsXQojR2VuZXJhdGUgY29ubmVjdGl2aXR5IG1hdHJpeCBmb3IgbmV1cm9uIHdlIGFyZSBpbnRlcmVzdGVkIGluCmNvbl9tYXQgPC0gY2F0bWFpZF9nZXRfY29ubmVjdG9yc19iZXR3ZWVuKHBvc3Rfc2tpZHMgPSBza2lkKQojc3Vic2V0IGNvbm5lY3Rpdml0eSBtYXRyaXggdG8gb25seSBjb250YWluIHBvaW50cyBpbiBuZXVyb24uZGlzdGFsLnBvaW50cwpuZXVyb24uZGlzdGFsLnBvaW50cyAjbmV1cm9uLmRpc3RhbC5wb2ludHMgY29udGFpbnMgdGhlIGZvbGxvd2luZzoKYGBgCmBgYHtyfQpjb25fbWF0ICNhbmQgdGhlIGNvbm5lY3Rpdml0eSBtYXRyaXggaXMgYSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGlzIGluZm9ybXRhdGlvbgpgYGAKYGBge3J9CiNkZXBlbmRpbmcgb24gd2hldGhlciB5b3Ugc3BlY2lmaWVkIHByZSBvciBwb3N0X3NraWRzIGluIHRoZSBjYXRtYWlkX2dldF9jb25uZWN0b3JzX2JldHdlZW4gd2UgY2FuIHN1YnNldCBjb25fbWF0IGJ5IHBvc3Rfbm9kZV9pZCBvciBwcmVfbm9kZV9pZC4gSW4gdGhpcyBleGFtcGxlIHdlIGFyZSB1c2luZyBwb3N0X3NraWRzIGFuZCBwb3N0X25vZGVfaWQgdG8gbG9vayBhdCBpbnB1dHMgdG8gb3VyIG5ldXJvbi4KCiNzdWJzZXQgY29uX21hdCBieSBub2RlaWQvUG9pbnRObyBmcm9tIG5ldXJvbi5kaXN0YWwucG9pbnRzCmNvbl9tYXQkcG9zdF9ub2RlX2lkICVpbiUgbmV1cm9uLmRpc3RhbC5wb2ludHMkUG9pbnRObyAgI3JldHVybnMgYSBsb2dpY2FsIG1hdHJpeCwgVFJVRSB2YWx1ZXMgYXJlIGNvbm5lY3Rpb25zIGluIG91ciBkaXN0YWwgYnJhbmNoLgpjb25fbWF0W2Nvbl9tYXQkcG9zdF9ub2RlX2lkICVpbiUgbmV1cm9uLmRpc3RhbC5wb2ludHMkUG9pbnRObyxdICNkb24ndCBmb3JnZXQgdGhlICIsIiBhcyBjb25fbWF0IGlzIGEgZGF0YWZyYW1lLgpkaXN0X2Nvbl9tYXQgPC0gY29uX21hdFtjb25fbWF0JHBvc3Rfbm9kZV9pZCAlaW4lIG5ldXJvbi5kaXN0YWwucG9pbnRzJFBvaW50Tm8sXQpgYGAKYGBge3J9CiNXZSBjYW4gZXZlbiBwbG90IGFsbCBvZiB0aGVzZSBjb25uZWN0aW9ucyB0byBkb3VibGUgY2hlY2sgdGhlIGNvbm5lY3Rpb25zIGFyZSBpbiB0aGUgcmlnaHQgcGxhY2UuCmNsZWFyM2QoKQpwbG90M2Qobiwgc29tYSA9IFQsIGNvbCA9ICJncmV5IikKcG9pbnRzM2QobmV1cm9uLmRpc3RhbC5wb2ludHNbLGMoJ1gnLCdZJywnWicpXSwgY29sID0gImRlZXBza3libHVlIikKcG9pbnRzM2QoZGlzdF9jb25fbWF0WyxjKCJjb25uZWN0b3JfeCIsICJjb25uZWN0b3JfeSIsICJjb25uZWN0b3JfeiIpXSwgY29sID0gImdyZWVuIiwgc2l6ZSA9IDUpCnJnbHdpZGdldCgpCmBgYAoKI1NvIHlvdSB3YW50IHRvIHBydW5lIGEgbmV1cm9uIG9iamVjdCBpbnN0ZWFkPyEKYGBge3J9CiNHcmVhdCEgVGhlcmUncyBhIGhhbmR5IGZ1bmN0aW9uIHN1YnNldCgpIGluIHRoZSBuYXQgcGFja2FnZS4KZGlzdC5uIDwtIHN1YnNldChuLCBkaXN0KQpuYW1lcyhkaXN0Lm4pCm5hbWVzKG4pCiNub3RlIGhvdyB0aGVyZSBpcyBsZXNzIGluZm9ybWF0aW9uIGluIHRoZSBkaXN0Lm4gbmV1cm9uIG9iamVjdC4gSXQgbG9vc2VzcyBjb25uZWN0aXZpdHkgaW5mb3JtYXRpb24uCmBgYAoKYGBge3J9CmNsZWFyM2QoKQpwbG90M2QoZGlzdC5uLCBXaXRoTm9kZXMgPSBGLCBjb2wgPSAiYmx1ZSIsIHNvbWEgPSBUKQpwbG90M2QobiwgY29sID0gImJsYWNrIiwgc29tYSA9IFQpCnJnbHdpZGdldCgpCmBgYAoKCiNUYXNrL0NoYWxsZW5nZTogVHJ5IGdlbmVyYXRlIGEgY29ubmVjdGl2aXR5IG1hdHJpeCBmb3IgdGhlIG90aGVyIGN1dCBwb2ludHMKIzIuIFVzZSB0aGUgZm9sbG93aW5nIGNvZGUgdG8gZ2VuZXJhdGUgYSBzYW1wbGluZyBzcHJlYWRzaGVldC4KCmBgYHtyfQojZ2l2ZXMgcmFuZG9tIG9yZGVyaW5nIG9mIHByZS1zeW5hcHRpYyBwcm9maWxlcwpub2RlID0gMjk0OTY0MzAKc2tpZCA9IDYwNjUyMTYKI2Z1bmN0aW9uIHRvIHJldHVybiB0aGUgaW5jb21pbmcgcHJlLXN5bmFwdGljIG5vZGVzIGZvciB0aGUgc2VsZWN0ZWQgc2tpZCBwcm94aW1hbCBvZiB0aGUgcHJvdmlkZWQgY3V0IHBvaW50CgppbmNvbWluZ19jb25uZWN0aW9ucyA8LSBmdW5jdGlvbihza2lkLCBub2RlKSB7CiAgbmV1cm9uIDwtIHJlYWQubmV1cm9uLmNhdG1haWQoc2tpZCkKICBkaXN0IDwtIGRpc3RhbF90byhuZXVyb24sIG5vZGUucG9pbnRubyA9IG5vZGUpCiAgbmV1cm9uLmRpc3RhbC5wb2ludHMgPC0gbmV1cm9uJGRbLWRpc3QsXQogIAogIAogIAogICNkZWJ1Zy0gZ3JhcGggdG8gY2hlY2sgc2VsZWN0ZWQgZGlzdGFsIHBvaW50cyBhcmUgaW4gY29ycmVjdCByZWdpb24KICBub3BlbjNkKCkKICBwbG90M2QobmV1cm9uLCBjb2wgPSAiZ3JheTIzIikKICBwb2ludHMzZChuZXVyb24uZGlzdGFsLnBvaW50c1ssYygnWCcsJ1knLCdaJyldLCBjb2wgPSAiZGVlcHNreWJsdWUiKQogIAogIGFsbF9jb25uZWN0b3JzIDwtIGNhdG1haWRfZ2V0X2Nvbm5lY3RvcnNfYmV0d2Vlbihwb3N0X3NraWRzID0gc2tpZCkKICBjb25uZWN0b3JzID0gYWxsX2Nvbm5lY3RvcnNbYWxsX2Nvbm5lY3RvcnMkcG9zdF9ub2RlX2lkICVpbiUgbmV1cm9uLmRpc3RhbC5wb2ludHMkUG9pbnRObyxdCiAgI2RlYnVnIC0gZ3JhcGggdG8gY2hlY2sgYWxsIHNlbGVjdGVkIGNvbm5lY3RvcnMgaW4gY29ycmVjdCByZWdpb24KICBub3BlbjNkKCkKICBwbG90M2QobmV1cm9uLCBjb2wgPSAiYmxhY2siKQogIHBvaW50czNkKGNvbm5lY3RvcnNbLGMoJ2Nvbm5lY3Rvcl94JywnY29ubmVjdG9yX3knLCdjb25uZWN0b3JfeicpXSkKICAKICByZXR1cm4oY29ubmVjdG9ycykKfQoKI2dldCBpbmNvbWluZyBzeW5hcHNlcyAKbl9pbmNvbWluZyA8LSBpbmNvbWluZ19jb25uZWN0aW9ucyhza2lkID0gc2tpZCwgbm9kZSA9IG5vZGUpCiNyYW5kb21pc2Ugb3V0Z29pbmcgY29ubmVjdG9ycyBmb3IgdHJhY2luZyBkb3duc3RyZWFtIHBhcnRuZXJzCnBlcm0gPSBuX2luY29taW5nW3NhbXBsZShucm93KG5faW5jb21pbmcpKSxdCiNkZWJ1ZyAtIApub3BlbjNkKCkKbmV1cm9uIDwtIHJlYWQubmV1cm9uLmNhdG1haWQoc2tpZCkKcGxvdDNkKG5ldXJvbikKcG9pbnRzM2Qobl9pbmNvbWluZ1ssYygncG9zdF9ub2RlX3gnLCdwb3N0X25vZGVfeScsJ3Bvc3Rfbm9kZV96JyldLCBjb2w9J2JsdWUnKQpwb2ludHMzZChwZXJtWyxjKCdwb3N0X25vZGVfeCcsJ3Bvc3Rfbm9kZV95JywncG9zdF9ub2RlX3onKV0sIGNvbCA9ICdncmVlbicsIHNpemUgPSA4KQoj4oCUVVJMIGdlbmVyYXRvcuKAlApjb25uZWN0b3JfVVJMIDwtIGZ1bmN0aW9uKGRmcm93KXsKICBiYXNlID0gImh0dHBzOi8vbmV1cm9waWwuamFuZWxpYS5vcmcvdHJhY2luZy9mYWZiL3YxNC8iCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoYmFzZSwgIj9waWQ9MSIpCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoY2F0bWFpZF91cmwsICImenA9IiwgZGZyb3dbLCJwcmVfbm9kZV96Il0pCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoY2F0bWFpZF91cmwsICImeXA9IiwgZGZyb3dbLCJwcmVfbm9kZV95Il0pCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoY2F0bWFpZF91cmwsICImeHA9IiwgZGZyb3dbLCJwcmVfbm9kZV94Il0pCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoY2F0bWFpZF91cmwsICImdG9vbD10cmFjaW5ndG9vbCIpCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoY2F0bWFpZF91cmwsICImYWN0aXZlX3NrZWxldG9uX2lkPSIsIGRmcm93WywncHJlX3NraWQnXSkKICBjYXRtYWlkX3VybCA9IHBhc3RlMChjYXRtYWlkX3VybCwgIiZhY3RpdmVfbm9kZV9pZD0iLCBkZnJvd1ssInByZV9ub2RlX2lkIl0pCiAgY2F0bWFpZF91cmwgPSBwYXN0ZTAoY2F0bWFpZF91cmwsICImc2lkMD01JnMwPTAiKQogIAogIGludmlzaWJsZShjYXRtYWlkX3VybCkKfQoj4oCU4oCU4oCUCiNnZW5lcmF0ZSBVUkxzIGZvciBlYWNoIHJvdwpwZXJtJFVSTCA9IGNoYXJhY3Rlcihucm93KHBlcm0pKQpwZXJtWywiVVJMIl0gPSBzYXBwbHkoMTpucm93KHBlcm0pLCBmdW5jdGlvbih4KSBwZXJtW3gsICJVUkwiXSA9IGNvbm5lY3Rvcl9VUkwocGVybVt4LF0pKQojd3JpdGUgb3V0IGFzIENTViB0byBzYXZlIGluIEdvb2dsZSBkb2NzCndyaXRlLmNzdihwZXJtLCBmaWxlID0gJ3Rlc3QuY3N2JykKCmxpYnJhcnkoZ29vZ2xlc2hlZXRzKQpnc19scygpCnk1IDwtIGdzX3RpdGxlKCJUSVRMRSBIRVJFIikgI0luc2VydCB5b3VyIHRpdGxlIGhlcmUKZ3NfZWRpdF9jZWxscyh5NSwgd3MgPSAxLCBpbnB1dCA9IHBlcm0kcHJlX3NraWQsIGFuY2hvciA9ICJBMyIsIGJ5cm93ID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFKQpnc19lZGl0X2NlbGxzKHk1LCB3cyA9IDEsIGlucHV0ID0gcGVybSRwb3N0X3NraWQsIGFuY2hvciA9ICJCMyIsIGJ5cm93ID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFKQpnc19lZGl0X2NlbGxzKHk1LCB3cyA9IDEsIGlucHV0ID0gcGVybSRjb25uZWN0b3JfaWQsIGFuY2hvciA9ICJDMyIsIGJ5cm93ID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFKQpnc19lZGl0X2NlbGxzKHk1LCB3cyA9IDEsIGlucHV0ID0gcGVybSRwb3N0X25vZGVfaWQsIGFuY2hvciA9ICJEMyIsIGJ5cm93ID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFKQpnc19lZGl0X2NlbGxzKHk1LCB3cyA9IDEsIGlucHV0ID0gcGVybSRVUkwsIGFuY2hvciA9ICJFMyIsIGJ5cm93ID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFKQoKYGBgCgoK